#include "MephistoCAM.h"

void init_dacx0508_config( DACx0508_Config *cfg ){

    // printf("**** init_dacx0508_config: start ****\n");
    
    cfg->reg_num   = _DACX0508_REG_NUM_;
    
    cfg->reg_addr[0] = 0x02;
    cfg->reg_addr[1] = 0x04;
    cfg->reg_addr[2] = 0x08;
    cfg->reg_addr[3] = 0x09;
    cfg->reg_addr[4] = 0x0A;
    cfg->reg_addr[5] = 0x0B;
    cfg->reg_addr[6] = 0x0C;
    cfg->reg_addr[7] = 0x0D;
    cfg->reg_addr[8] = 0x0E;
    cfg->reg_addr[9] = 0x0F;

    cfg->reg_data[0] = 0;   // default value
    cfg->reg_data[1] = 255; // default value
    
//  the followings will be updated from *ini
    cfg->reg_data[2] = 0;
    cfg->reg_data[3] = 0;
    cfg->reg_data[4] = 0;
    cfg->reg_data[5] = 0;
    cfg->reg_data[6] = 0;
    cfg->reg_data[7] = 0;
    cfg->reg_data[8] = 0;
    cfg->reg_data[9] = 0;
    
    int i;
    for( i=0; i<8; i++ ){
        cfg->gain[i] = 1;
        cfg->vout[i] = 0;
    }
    
    cfg->steps         = _DEFAULT_STEPS_;          // 10
    cfg->sleep_time_us = _DEFAULT_SLEEP_TIME_US_;  // 100ms

    // printf("**** init_dacx0508_config: end ****\n");
}

void read_dacx0508_config( DACx0508_Config *cfg, 
                            char *cfg_ini, 
                            char *section ){

    init_dacx0508_config( cfg );    // 初始化之后再从参数文件中读取参数    
    
    dictionary *ini = iniparser_load(cfg_ini);
    
    if( ini == NULL ){
        char ErrMsg[1024];
        sprintf(ErrMsg, "cannot parse file: %s\n", cfg_ini);
        printDebugInfo(ErrMsg);
        exit(0);
    }
    
    char sec_param[64];

    sprintf(sec_param, "%s:steps", section);
    cfg->steps          = iniparser_getint(ini, sec_param, _DEFAULT_STEPS_);
    
    sprintf(sec_param, "%s:sleep_time_us", section);
    cfg->sleep_time_us  = iniparser_getint(ini, sec_param, _DEFAULT_SLEEP_TIME_US_);
    

    sprintf(sec_param, "%s:is_single", section);
    cfg->is_single      = iniparser_getdouble(ini, sec_param, 1);
    
    int i;
    for( i=0; i<8; i++ ){
        sprintf(sec_param, "%s:gain_%d", section, i);
        cfg->gain[i]    = iniparser_getdouble(ini, sec_param, 1.0);
        
        sprintf(sec_param, "%s:vout_%d", section, i);
        cfg->vout[i]    = iniparser_getdouble(ini, sec_param, 0.0);
        cfg->vout_f[i]  = cfg->vout[i];
    }

    if( cfg->is_single == 1 ){
    //  对于单芯片的DAC70508而言，需要修正两路输出
        // correct vout[0] with vout[1]
        cfg->vout[0] = cfg->vout[1] - cfg->vout[0];

        // correct vout[4] with vout[5]
        cfg->vout[4] = cfg->vout[5] - cfg->vout[4];
    }

    for( i=0; i<8; i++ ){
        cfg->reg_data[i+2] = get_dac_code(cfg->vout[i], cfg->gain[i]);
    }
    
    sprintf(cfg->cfg_fname, "%s", cfg_ini);
    sprintf(cfg->cfg_section, "%s", section);
    
    // printf("\n> finished read params from: %s [section:%s]\n", cfg_ini, section);

    iniparser_freedict( ini );
}

void free_dacx0508_config( DACx0508_Config *cfg ){
  // do nothing ...
}

//  复制配置参数
int duplicate_DACx0508( DACx0508_Config *cfg_old, DACx0508_Config *cfg_new ){
    
    int i;

    // copy scalars
    cfg_new->is_single      = cfg_old->is_single;
    cfg_new->reg_num        = cfg_old->reg_num;
    cfg_new->steps          = cfg_old->steps;
    cfg_new->sleep_time_us  = cfg_old->sleep_time_us;


    // copy reg_addr[10] & reg_data[10]
    for( i=0; i<10; i++ ){
        cfg_new->reg_addr[i] = cfg_old->reg_addr[i];
        cfg_new->reg_data[i] = cfg_old->reg_data[i];
    }

    // copy gain[8] & vout[8] & vout_f[8]
    for( i=0; i<8; i++ ){
        cfg_new->gain[i]    = cfg_old->gain[i];
        cfg_new->vout[i]    = cfg_old->vout[i];
        cfg_new->vout_f[i]  = cfg_old->vout_f[i];
    }

    // copy string arrays
    sprintf(cfg_new->cfg_fname, "%s", cfg_old->cfg_fname);
    sprintf(cfg_new->cfg_section, "%s", cfg_old->cfg_section);

    return 0;
}

//  convert user-desired 'Vout=ADC_out x Gain' to 'DAC_code'
//
//  @2021年12月29日：
//  对于DAC80508而言，DAC code为16bits
//  对于DACx0508而言，DAC code为14bits，但是在SPI通信时仍采用16bits，
//  只不过是只使用高位的14bits，所以实际上下面的代码根本不需要进行修改！！！
uint32_t get_dac_code( double vout, double gain ){

    uint32_t code_uint = (uint32_t)( (vout/gain)/5.0*_CODE_VAL_MAX_);
    
    if( code_uint > _CODE_VAL_MAX_ ){   // for DACx0508, MAX(code) = 2^16-1 = 65535
        char msg[1024];
        sprintf(msg,"(dacx0508) derived DAC code : %d is greter than 2^16-1!", code_uint);
        printDebugInfo(msg);
        exit(0);
    }
    
    return code_uint;
}

//  print DACx0508 configurations for debug
int print_dacx0508_config( DACx0508_Config *cfg ){
    
    printf("> DACx0508 configuration information:\n");
    printf("> ini_fname: %s\n", cfg->cfg_fname);
    printf("> section: %s\n", cfg->cfg_section);
    
    printf("> 0X%x %d\n", cfg->reg_addr[0], cfg->reg_data[0]);
    printf("> 0X%x %d\n", cfg->reg_addr[1], cfg->reg_data[1]);
    
    int i;
    for( i=0; i<8; i++ ){
        printf("> 0X%x %6d, gain_%d = %4.3f vout_%d = %6.3f vout_f_%d = %6.3f\n", 
                    cfg->reg_addr[2+i], 
                    cfg->reg_data[2+i],
                    i,
                    cfg->gain[i],
                    i,
                    cfg->vout[i],
                    i,
                    cfg->vout_f[i]
                );
    }
    
    return 0;
}

//  update configuration for a 'opt' with value 'opt_val'
int update_dacx0508_config( DACx0508_Config *cfg, 
                            int mode,
                            char* opt, 
                            int opt_val ){
//  not implemented yet!
    return 0;
}

int config_dacx0508_via_SPI(uint32_t *gpio_reg, 
					        uint32_t *gpio2_reg, 
					        uint32_t *gpio_in_reg,
					        SPI_CONFIG *spi_config,
					        DACx0508_Config *cfg,
                            uint32_t state
					        ){

    int step;
    double *scale = (double*)malloc(sizeof(double)*cfg->steps);

    if( state == 0 ){ // 逐渐递增
        printf("> (dacx0508) powering down ...\n");
        int i;
        for(i=0; i<cfg->steps; i++){
            scale[i] = 1.0 - (i+1.0) / cfg->steps;
        }
    } else if( state == 1 ){    // 逐渐递减
        printf("> (dacx0508) powering up ...\n");
        int i;
        for(i=0; i<cfg->steps; i++){
            scale[i] = (i+1.0) / cfg->steps;
        }
    }

	int used_reg_num        = cfg->reg_num;
    SPI_DATA *spi_data      = (SPI_DATA*)malloc(sizeof(SPI_DATA));  // prepare SPI data to be transferred
    spi_data_alloc(spi_data, used_reg_num);                         // 准备好SPI数据
        
    for( step=0 ; step<cfg->steps; step++ ){

#if defined(_DEBUG_DACX0508_)
        printf("> (dacx0508) --> %g%%\n", 100*scale[step]);
#endif
    	int idx;
        for( idx=0; idx<cfg->reg_num; idx++ ){
            uint32_t addr = 0;
            addr |= ( 0 << 7 );                     // set R/W
            addr |= ( 0 << 6 );                     // reserved bit
            addr |= ( 0 << 5 );                     // reserved bit
            addr |= ( 0 << 4 );                     // reserved bit
            addr |= cfg->reg_addr[idx];             // A3A2A1A0
            
            uint32_t data = cfg->reg_data[idx];
            
            if( idx >= 2 ){
                data = (uint32_t)( cfg->reg_data[idx] * scale[step] );
            }

            spi_data_set_LS( spi_data, 
                             idx, 
                             addr, 
                             data,
                             16 );  // left-shifted 16bits
        }
        
    //  将SPI数据发送至DACx0508
        send_spi_data(  gpio_reg,
                        gpio2_reg,
                        gpio_in_reg,
                        spi_config, 
                        spi_data );
                        
        usleep(cfg->sleep_time_us);
    }

    free(scale);
    spi_data_free(spi_data);	
    
    return 0;				        
}
